<?php

declare(strict_types=1);

namespace App\Aspect;

use App\Annotation\SystemLog;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Logger\LoggerFactory;
use HyperfExt\Jwt\Contracts\JwtFactoryInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use App\Repository\Web\SystemLogsRepository;
use Hyperf\Di\Annotation\Inject;

/**
 * @Aspect
 */
#[Aspect]
class SystemLogAspect extends AbstractAspect
{
    protected RequestInterface $request;
    public $annotations = [
        SystemLog::class,
    ];

    private SystemLogsRepository $systemLogs;
    private LoggerInterface $logger;
    #[Inject]
    private JwtFactoryInterface $jwtFactory;
    public function __construct(LoggerFactory $loggerFactory,RequestInterface $request, SystemLogsRepository $systemLogs)
    {
        $this->logger = $loggerFactory->get('log', 'default');
        $this->request = $request;
        $this->systemLogs = $systemLogs;
    }

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 获取注解
        $annotation = $proceedingJoinPoint->getAnnotationMetadata();
        /** @var SystemLog $logAnnotation */
        $logAnnotation = $annotation->method[SystemLog::class] ?? null;

        if (!$logAnnotation) {
            return $proceedingJoinPoint->process();
        }
        $className = $proceedingJoinPoint->className;
        $methodName = $proceedingJoinPoint->methodName;

        $this->logger->info("开始执行方法: {$className}::{$methodName}");

        // 记录方法入参
        $params = [];
        if ($logAnnotation->withParams) {
            $params = $this->request->post();
        }
        try {
            // 执行原方法
            $result = $proceedingJoinPoint->process();

            $token = $result['token'] ?? $this->getTokenFromRequest();
            if (!$token) {
                return $result;
            }
            $payload = $this->jwtFactory->make()->setToken($token)->getPayload();
            $userId = $payload->get('sub');

            $userAgent = $this->request->header('user-agent', 'Unknown');
            // 记录方法返回结果
            $logData = [
                'user_id' => $userId,
                'action' => "{$className}::{$methodName}",
                'description' => $logAnnotation->description,
                'ip_address' => $this->getClientIp(),
                'user_agent' => $userAgent,
                'request_data' => json_encode($params),
                'response_data' => json_encode($result),
            ];

            if ($logAnnotation->withResult) {
                $logData['result'] = $result;
            }
            // 记录日志方法
            $this->systemLogs->create($logData);

            return $result;
        } catch (Throwable $e) {
            $this->logger->info("操作失败,不记录日志: ".$e->getMessage().$e->getFile().$e->getFile());
            throw $e;
        }
    }

    /**
     * 获取客户端IP
     */
    public function getClientIp(): string
    {
        $headers = $this->request->getHeader('x-forwarded-for');
        if (!empty($headers)) {
            return $headers[0];
        }
        $serverParams = $this->request->getServerParams();
        return $serverParams['remote_addr'] ?? '127.0.0.1';
    }
    protected function getTokenFromRequest(): ?string
    {
        $token = $this->request->getHeaderLine('Authorization');
        if (!empty($token) && preg_match('/Bearer\s*(\S+)\b/i', $token, $matches)) {
            return $matches[1];
        }

        $token = $this->request->getQueryParams()['token'] ?? null;
        if (!empty($token) && is_string($token)) {
            return $token;
        }

        return null;
    }
}